<template>
  <section class="table-box">
    <header class="aside-tools-box rotate90">
      <el-tooltip content="表格设置" placement="left">
        <el-button class="aside-tools-btn" round icon="el-icon-download" @click="drawer = true"/>
      </el-tooltip>
    </header>
    <el-table ref="table" v-bind="$attrs" v-on="$listeners">
      <!--   多选和索引渲染插槽   -->
      <slot/>
      <template v-for="(setup, key) in setupList">
        <el-table-column
            v-if="setup.show"
            :key="setup.prop + key"
            show-overflow-tooltip
            v-bind="setup"
        >
          <template #default="{$index,column,isExpanded,isSelected,row,store,_self}">
            <!--      render函数式渲染      -->
            <template v-if="setup.render">
              <expand-dom
                  :cellValue="row[setup.prop]"
                  :column="column"
                  :columnIndex="key"
                  :isExpanded="isExpanded"
                  :isSelected="isSelected"
                  :row="row"
                  :rowIndex="$index"
                  :setup="setup"
                  :store="store"
                  :_self="_self"
              />
            </template>
            <!--      formatter函数式渲染      -->
            <template v-else-if="setup.formatter">
              <HeightText
                  :search="search"
                  :text="
                setup.formatter({
                  cellValue: row[setup.prop],
                  column,
                  columnIndex: key,
                  isExpanded,
                  isSelected,
                  row,
                  rowIndex: $index,
                  setup,
                  store,
                  _self
                })
              "
              />
            </template>
            <!--      插槽式渲染      -->
            <template v-else-if="setup.slotName">
              <slot
                  :name="setup.slotName"
                  v-bind="{
                  cellValue: row[setup.prop],
                  column,
                  columnIndex: key,
                  isExpanded,
                  isSelected,
                  row,
                  rowIndex: $index,
                  setup,
                  store,
                  _self
                }"
              />
            </template>
            <!--      无任何处理      -->
            <template v-else>
              <HeightText :search="search" :text="row[setup.prop]"/>
            </template>
          </template>
        </el-table-column>
      </template>
      <!--   插入至表格最后一行之后的内容，如果需要对表格的内容进行无限滚动操作，可能需要用到这个 slot。若表格有合计行，该 slot 会位于合计行之上。   -->
      <template #append>
        <slot name="append"/>
      </template>
    </el-table>
    <el-drawer title="表格设置" :visible.sync="drawer" :modal="false">
      <el-card class="titleBox" v-show="nomalColumnsLength">
        <template #header>默认列</template>
        <VueDraggable v-model="setupList" ghostClass="ghost" handle=".move" :animation="150">
          <div v-for="item in setupList" :key="item.prop + 'nomal'" v-show="item.fixed === undefined"
               class="checkboxItem">
            <i class="el-icon-more rotate90 move"/>
            <el-link type="primary" :underline="false">{{ item.label }}</el-link>
            <el-switch v-model="item.show"/>
            <el-select v-model="item.fixed" size="mini">
              <el-option :value="undefined" label="默认"/>
              <el-option value="left" label="左侧固定"/>
              <el-option value="right" label="右侧固定"/>
            </el-select>
          </div>
        </VueDraggable>
      </el-card>
      <el-card class="titleBox" v-show="leftFixedColumnsLength">
        <template #header>左侧固定列</template>
        <VueDraggable v-model="setupList" ghostClass="ghost" handle=".move" :animation="150">
          <div v-for="item in setupList" :key="item.prop + 'left'" v-show="item.fixed === 'left'" class="checkboxItem">
            <i class="el-icon-more rotate90 move"/>
            <el-link type="primary" :underline="false">{{ item.label }}</el-link>
            <el-switch v-model="item.show"/>
            <el-select v-model="item.fixed" size="mini">
              <el-option :value="undefined" label="默认"/>
              <el-option value="left" label="左侧固定"/>
              <el-option value="right" label="右侧固定"/>
            </el-select>
          </div>
        </VueDraggable>
      </el-card>
      <el-card class="titleBox" v-show="rightFixedColumnsLength">
        <template #header>右侧固定列</template>
        <VueDraggable v-model="setupList" ghostClass="ghost" handle=".move" :animation="150">
          <div v-for="item in setupList" :key="item.prop + 'right'" v-show="item.fixed === 'right'"
               class="checkboxItem">
            <i class="el-icon-more rotate90 move"/>
            <el-link type="primary" :underline="false">{{ item.label }}</el-link>
            <el-switch v-model="item.show"/>
            <el-select v-model="item.fixed" size="mini">
              <el-option :value="undefined" label="默认"/>
              <el-option value="left" label="左侧固定"/>
              <el-option value="right" label="右侧固定"/>
            </el-select>
          </div>
        </VueDraggable>
      </el-card>
    </el-drawer>
  </section>
</template>

<script>
import HeightText from './HeightText.vue'
import {VueDraggable} from 'vue-draggable-plus'

export default {
  name: 'YTable',
  components: {
    // 生成元素自定义组件，render函数式组件渲染
    expandDom: {
      // 函数式组件,无状态 (没有响应式数据)，也没有实例 (没有 this 上下文)
      functional: true,
      props: {
        cellValue: String,
        column: Object,
        columnIndex: Number,
        isExpanded: Boolean,
        isSelected: Boolean,
        row: Object,
        rowIndex: Number,
        setup: Object,
        store: Object,
        _self: Object
      },
      render: (h, ctx) => {
        return ctx.props.setup.render(h, ctx.props)
      },
    },
    HeightText,
    VueDraggable
  },
  props: {
    // 用于渲染表格的基础内容，必传字段，表头数据 Table-column Attributes
    columns: {
      type: Array,
      validator: function (value) {
        return value.every((item) => {
          // 必传属性
          if (typeof item.prop !== 'string' || typeof item.label !== 'string') {
            return false;
          }

          // 非必传属性
          if (item.width !== undefined && typeof item.width !== 'string') {
            return false;
          }

          if (item.slotName !== undefined && typeof item.slotName !== 'string') {
            return false;
          }

          if (item.render !== undefined && typeof item.render !== 'function') {
            return false;
          }
          // 限制fixed属性只能填写 left 或者 right 或者 undefined
          if (![undefined, 'left', 'right'].includes(item.fixed)) {
            return false;
          }

          return !(item.formatter !== undefined && typeof item.formatter !== 'function');
        });
      },
      required: true,
    },
    // 用于搜索匹配高亮关键字
    search: {
      type: String,
      default: '',
    },
    // ...其余属性同 ElementUI 表格 Attributes 直接绑定传入即可
  },
  data() {
    return {
      setupList: [],
      drawer: false,
    }
  },
  computed: {
    nomalColumnsLength() {
      return this.setupList.filter(item => item.fixed === undefined).length
    },
    leftFixedColumnsLength() {
      return this.setupList.filter(item => item.fixed === 'left').length
    },
    rightFixedColumnsLength() {
      return this.setupList.filter(item => item.fixed === 'right').length
    },
  },
  updated() {
    // 更新时，解决错位问题，修复滚动条丢失的 bug
    this.$nextTick(() => {
      this.$refs.table.doLayout()
    })
  },
  activated() {
    // 激活时，解决错位问题，修复滚动条丢失的 bug
    this.$nextTick(() => {
      this.$refs.table.doLayout()
    })
  },
  created() {
    this.setupList = [...this.columns]
  },
  mounted() {
    // 暴露 el-table 组件原有方法给外层 $refs，或者使用 this.$refs[外层ref名].$refs.table.xxx() 调用
    const entries = Object.entries(this.$refs.table)
    for (const [key, value] of entries) {
      // 排除含有 $ 或 _ 的属性，防止浏览器页面卡顿
      if (!(key.includes('$') || key.includes('_'))) {
        this[key] = value
      }
    }
  },
}
</script>

<style scoped>
.table-box {
  --tools-size: 16px;
  position: relative;
}

.aside-tools-box {
  position: absolute;
  top: var(--tools-size);
  right: calc(var(--tools-size) * -1);
  z-index: 999;
}

.aside-tools-btn {
  padding: 0 var(--tools-size);
}

.rotate90 {
  transform: rotate(90deg);
}

.titleBox {
  margin: 0 12px;
}

.checkboxItem {
  display: flex;
  align-content: center;
  gap: var(--gap-S);
  background-color: #6b72800d;
  border-radius: .5rem;
  padding: .75rem;
  height: 1.5rem;
  margin-bottom: 10px;
  margin-right: 30px;
}

.move {
  cursor: move;
}

.ghost {
  opacity: 0.5;
  background: #c8ebfb;
}
</style>